package com.jf.identity.datasource;

import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.jf.identity.config.ExamConfig;
import com.jf.identity.config.MachineConfig;
import com.jf.identity.entity.ExamInfo;
import org.apache.commons.dbcp2.BasicDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.*;

/**
 * @author FCG
 */
@Component
public class TargetDataSource {
    private final static String driverClassName = "com.mysql.jdbc.Driver";
    private final Logger logger = LoggerFactory.getLogger(getClass());
    private final static String dbConnUrlStr
            = "?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false" +
            "&allowMultiQueries=true&useSSL=true";
    private final static String dataBaseHost = "192.168.20.195";
    private final static String dataBasePort = "3306";
    private final static String userName = "root";
    private final static String pwd = "jfkjyfb";
    public final static String jfVisDb = "jf_vis_db";
    public final static String jfDcDb = "jf_dc_db";
    public final static String jfExdasDb = "jf_exdas_db";
    public final static String jfExedcDb = "jf_exedc_db";

    /**
     * 根据machineConfig中的配置，生成多个待用数据源
     *
     * @param machineConfig machineConfig
     * @return 多个数据源
     */
    public Map<String, DataSource> getTargetDataSource(MachineConfig machineConfig, ExamConfig examConfig) {
        Objects.requireNonNull(machineConfig, "machineConfig can't be null");
        Map<String, DataSource> targetDataSource = new HashMap<>();
        List<ExamInfo> examInfos = examConfig.getExamInfos();
        //数据库连接字符串
        //jdbc:mysql://192.168.20.195:3306/###?user=root&amp;password=jfkjyfb&amp;useUnicode=true&amp;characterEncoding=utf8&amp;autoReconnect=true&amp;failOverReadOnly=false&amp;allowMultiQueries=true
        String tempUrl = machineConfig.getDbConnectionUrl();
        // 静态库
        String jfVisUrl = tempUrl.replace("###", TargetDataSource.jfVisDb);
        AtomikosDataSourceBean jfVisDb = getAxDataSource(TargetDataSource.jfVisDb, jfVisUrl);
        targetDataSource.put(TargetDataSource.jfVisDb, jfVisDb);

        //配置 分库
        for (ExamInfo examInfo : examInfos) {
            //考试计划id
            String examId = examInfo.getExamId();
            //分库 jf_vis_db_20181009
            String url = tempUrl.replace("###", TargetDataSource.jfVisDb + "_" + examId);
//            BasicDataSource jfVisDb = getDataSource();
//            jfVisDb.setUrl(url);
            AtomikosDataSourceBean jfVisExamIdDb = getAxDataSource(TargetDataSource.jfVisDb + examId, url);
            targetDataSource.put(TargetDataSource.jfVisDb + examId, jfVisExamIdDb);
        }
        //配置 基础库
//        BasicDataSource jfDcDb = getDataSource();
        String jfDcDbUrl = tempUrl.replace("###", TargetDataSource.jfDcDb);
        AtomikosDataSourceBean jfDcDb = getAxDataSource(TargetDataSource.jfDcDb, jfDcDbUrl);
//        jfDcDb.setUrl(url);
        targetDataSource.put(TargetDataSource.jfDcDb, jfDcDb);

        //配置 考务库
//        BasicDataSource jfExdasDb = getDataSource();
        String exdasUrl = tempUrl.replace("###", TargetDataSource.jfExdasDb);
        AtomikosDataSourceBean jfExdasDb = getAxDataSource(TargetDataSource.jfExdasDb, exdasUrl);
//        jfExdasDb.setUrl(exdasUrl);
        targetDataSource.put(TargetDataSource.jfExdasDb, jfExdasDb);
        //配置 jf_exedc_db
//        BasicDataSource jfExedcDb = getDataSource();
        String exedcUrl = tempUrl.replace("###", TargetDataSource.jfExedcDb);
        AtomikosDataSourceBean jfExedcDb = getAxDataSource(TargetDataSource.jfExedcDb, exedcUrl);
//        jfExedcDb.setUrl(exedcUrl);
        targetDataSource.put(TargetDataSource.jfExedcDb, jfExedcDb);
        return targetDataSource;
    }

    public AtomikosDataSourceBean getDefaultDataSource(MachineConfig machineConfig) {
        String url = machineConfig.getDbConnectionUrl();
        //默认数据库 jf_vis_db
        url = url.replace("###", TargetDataSource.jfVisDb);
//        BasicDataSource dataSource = getDataSource();
//        dataSource.setUrl(url);
        AtomikosDataSourceBean dataSourceBean = getAxDataSource("default", url);
        return dataSourceBean;
    }

    /**
     * 配置 ax数据源
     *
     * @param uniqueResourceName 名称
     * @param dbConnUrlStr       连接url
     * @return 数据源
     */
    private AtomikosDataSourceBean getAxDataSource(String uniqueResourceName, String dbConnUrlStr) {
        logger.info("设置数据源:{}", dbConnUrlStr);
        AtomikosDataSourceBean dataSourceBean = new AtomikosDataSourceBean();
        Properties xaProperties = new Properties();
        xaProperties.setProperty("url", dbConnUrlStr);
        dataSourceBean.setUniqueResourceName(uniqueResourceName);
        dataSourceBean.setXaDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlXADataSource");
        dataSourceBean.setPoolSize(3);
        dataSourceBean.setMinPoolSize(1);
        dataSourceBean.setMaxPoolSize(10);
        dataSourceBean.setXaProperties(xaProperties);
        return dataSourceBean;
    }

    /**
     * 设置一些通用配置
     *
     * @return druid 数据源
     */
    private BasicDataSource getDataSource() {
        BasicDataSource dataSource = new BasicDataSource();
//        dataSource.setUsername(userName);
//        dataSource.setPassword(pwd);
        dataSource.setDriverClassName(driverClassName);
        //配置初始化大小、最小、最大
        dataSource.setInitialSize(0);
        dataSource.setMinIdle(0);
        dataSource.setMaxTotal(10);
        //配置获取连接等待超时的时间
        dataSource.setMaxWaitMillis(60000);
        //这里建议配置为TRUE，防止取到的连接不可用。获取链接的时候，不校验是否可用，开启会有损性能
        dataSource.setTestOnBorrow(true);
        //归还链接到连接池的时候校验链接是否可用
        dataSource.setTestOnReturn(true);
        //此项配置为true即可，不影响性能，并且保证安全性。
        // 意义为：申请连接的时候检测，如果空闲时间大于timeBetweenEvictionRunsMillis，执行validationQuery检测连接是否有效
        dataSource.setTestWhileIdle(true);
        //配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位毫秒
        dataSource.setTimeBetweenEvictionRunsMillis(60000);
        //配置一个连接在池中最小生存的时间，单位毫秒
        dataSource.setMinEvictableIdleTimeMillis(300000);
        //链接使用超过时间限制是否回收
        dataSource.setRemoveAbandonedOnBorrow(true);
        //超过时间限制时间（单位秒），目前为5分钟，如果有业务处理时间超过5分钟，可以适当调整。
        dataSource.setRemoveAbandonedTimeout(300);
        //链接回收的时候控制台打印信息，测试环境可以加上true，线上环境false。会影响性能。
        dataSource.setLogAbandoned(true);
        //验证连接有效与否的SQL，不同的数据配置不同
        dataSource.setValidationQuery("SELECT 1");
        return dataSource;
    }
}




















